package com.df.my_net_game.game_rpc.config;

import com.df.my_net_game.common.config.MethodMetaInfo;
import com.df.my_net_game.common.utils.SnowflakeIdGenerator;
import com.df.my_net_game.common.utils.SnowflakeWorkIdAndDatacenterIdUtils;
import com.df.my_net_game.game_rpc.EzGameMsg;
import com.df.my_net_game.game_rpc.RpcService;
import com.df.my_net_game.game_rpc.ServerNode;
import io.netty.util.concurrent.Promise;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * RPC服务管理器
 * 1. 管理所有RPC服务实例
 * 2. 处理服务发现与注册
 * 3. 消息路由与负载均衡
 */
public class GRpcServiceMgr {
    private static final Map<String, RpcService> rpcServiceMap = new ConcurrentHashMap<>();
    private static final Map<String, ServerNode> directConnections = new ConcurrentHashMap<>();
    private static final AtomicLong requestIdGenerator = new AtomicLong(0);

    private static SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(SnowflakeWorkIdAndDatacenterIdUtils.getWorkerId(),SnowflakeWorkIdAndDatacenterIdUtils.getDatacenterId());

    public static void registerService(String serviceName, RpcService service) {
        rpcServiceMap.put(serviceName, service);
    }

    public static long nextSnowId(){
        return idGenerator.nextId();
    }

    public static Promise<EzGameMsg> sendMsg(MethodMetaInfo methodMetaInfo, Object[] args)  {
        String ip = methodMetaInfo.getIp();
        if (ip == null || ip.isEmpty()) {
            return sendToCluster(methodMetaInfo.getServer(), methodMetaInfo, args);
        }
        return sendToSpecificNode(methodMetaInfo.getServer(), methodMetaInfo, args, ip,methodMetaInfo.getPort());
    }

    private static Promise<EzGameMsg> sendToCluster(String serviceName, MethodMetaInfo methodMetaInfo, Object[] args)  {
        RpcService service = rpcServiceMap.get(serviceName);
        if (service == null) {
            throw new RpcException("未找到服务: " + serviceName);
        }

        //TODO 如果是广播,则给所有服务节点发送消息
        if(methodMetaInfo.isBroadcast()){
            //TODO 广播的化支持返回list或void
            return null;
        }else {
            for (int retry = 0; retry <= methodMetaInfo.getRetryTimes(); retry++) {
                ServerNode node = null;

                try {
                    node = service.selectNode();
//                    if (methodMetaInfo.isAsync()) {
//                        return node.sendMsgAync(methodMetaInfo, args);
//                    }
//                    return node.sendMsgSync(methodMetaInfo, args);
                    //所有接口都返回Promise或void
                      return node.sendMsgAync(methodMetaInfo, args);
                } catch (Exception e) {
                    if(node != null) {
                        node.markUnhealthy();
                    }
                    if (retry == methodMetaInfo.getRetryTimes()) {
                        e.printStackTrace();
                        return null;
                    }
                }
            }
            return null;
        }

    }

    private static Promise<EzGameMsg> sendToSpecificNode(String serviceName, MethodMetaInfo methodMetaInfo,
                                                         Object[] args, String ip, int port)  {
        String key  = ip+":"+port;
        ServerNode node = directConnections.computeIfAbsent(key, addr -> {
            ServerNode newNode = new ServerNode(addr);
            newNode.setServiceName(serviceName);
            return newNode;
        });

        for (int i = 0; i <= methodMetaInfo.getRetryTimes(); i++) {
            try {
//            if (methodMetaInfo.isAsync()) {
//                return node.sendMsgAync(methodMetaInfo, args);
//            }
//            return node.sendMsgSync(methodMetaInfo, args);
                return node.sendMsgAync(methodMetaInfo, args);
            } catch (Exception e) {
                if(i==methodMetaInfo.getRetryTimes()) {//达到重试次数
                    e.printStackTrace();
                    directConnections.remove(key);
                    node.close();
                    return null;
                }
            }
        }
        return null;
    }

    public static long getReqId(String serviceName) {
        return requestIdGenerator.incrementAndGet();
    }

    public static void healthCheck() {
        directConnections.values().removeIf(node -> {
            if (!node.checkAlive(5000)) {
                node.close();
                return true;
            }
            return false;
        });
    }
}

class RpcException extends RuntimeException {
    public RpcException(String message) { super(message); }
    public RpcException(String message, Throwable cause) { super(message, cause); }
}
